package jfprincesstests.goaltests;

import java.util.*;
import java.text.*;

import laboratoryQ.junit.ignore.patch.BaseTestCase;
import laboratoryQ.computation.BaseUtils;
import jfuzzyprincepless.agents.INegotiatingAgent;
import jfuzzyprincepless.agents.BaseNegotiationAgent;
import jfuzzyprincepless.zarchive.algorithm.structure.ICommandConstruction;
import jfuzzyprincepless.zarchive.algorithm.structure.forcnstr.NegotiationForAbsEach;
import jfuzzyprincepless.zarchive.algorithm.structure.ifcnstr.INegotiationCondition;
import jfuzzyprincepless.zarchive.algorithm.structure.ifcnstr.NegotiationAndCondition;
import jfuzzyprincepless.zarchive.algorithm.structure.ifcnstr.NegotiationIfConstruction;
import jfuzzyprincepless.zarchive.negotiation.services.IExecuteForService;
import jfuzzyprincepless.negotiation.IExecutionContext;
import jfuzzyprincepless.negotiation.INegotiationContext;
import jfuzzyprincepless.negotiation.services.IService;

class Device {
	private String name;

	public double signalStrongness;
	public boolean sndRecvSMS;

	public boolean hasAlarm;
	public boolean hasReminder;

	public double reproducerQuality;
	public double musicMemorySize; 
	public boolean ableToPlayMp3;
	
	// to be easy -- ignore photo memory

	public double photoResolutionX;
	public double photoResolutionY;
	public double photoContrast;

	public double price;

	Device(
		String name,
		double signalStrongness,
		boolean sndRecvSMS,
		boolean hasAlarm,
		boolean hasReminder,
		double reproducerQuality,
		double musicMemorySize,
		boolean ableToPlayMp3,
		double photoResolutionX,
		double photoResolutionY,
		double photoContrast,
		double price
	) {
		this.name = name;
		this.signalStrongness = signalStrongness;
		this.sndRecvSMS = sndRecvSMS;
		this.hasAlarm = hasAlarm;
		this.hasReminder = hasReminder;
		this.reproducerQuality = reproducerQuality;
		this.musicMemorySize = musicMemorySize;
		this.ableToPlayMp3 = ableToPlayMp3;
		this.photoResolutionX = photoResolutionX;
		this.photoResolutionY = photoResolutionY;
		this.photoContrast = photoContrast;
		this.price = price;
	}

	@Override
	public String toString() {
		return "Device: " + name;
	}

	public String getName() {
		return name;
	}
}

enum DeviceNegotiation {
	COMPUTE_CHARACTIRISTICS,
	COLLECT_COMPUTATIONS,
	FIND_FRIENDS,
}

interface IDeviceMarksServ extends IService {
	public void registerMark(
		INegotiatingAgent agent,
		Device device,
		double mark /* 0.0 .. 1.0 */
	);

	public double[] getMarks(INegotiatingAgent criterionGroupAgent, Device device);

	public void registerCriterionAval(
		INegotiatingAgent criterionAgent,
		Device dev,
		double mark
	);

	public double getCriterionAval(INegotiatingAgent criterionAgent, Device dev);

	public Iterable<INegotiatingAgent> getCriterions();
}

interface IDevCollectionsServ extends IService {
	public Iterable<Set<Device>> getCollections();
	public void addCollection(Set<Device> extendedSet);
}

class StoreDevCollections
	extends BaseNegotiationAgent
	implements IDevCollectionsServ
{
	private Set<Set<Device>> collectionsSet =
		new LinkedHashSet<Set<Device>>();
	private Set<Set<Device>> buffer =
		new LinkedHashSet<Set<Device>>();

	@Override
	public Iterable<Set<Device>> getCollections() {
		collectionsSet.addAll(buffer);
		buffer.clear();
		return collectionsSet;
	}

	@Override
	public void addCollection(Set<Device> extendedSet) {
		buffer.add(extendedSet);
	}
}

class StoreDeviceMarksAgent
	extends BaseNegotiationAgent
	implements IDeviceMarksServ
{
	private static final double EPS = 1e-5;
	private static final double[] EMPTY_MARKS = new double[0];

	private Map<INegotiatingAgent, Map<Device, Double>> crag2devs =
		new HashMap<INegotiatingAgent, Map<Device, Double>>();
	private double defaultVal = -Double.MAX_VALUE;
	private Map<INegotiatingAgent, Map<Device, Double>> ag2mark =
		new HashMap<INegotiatingAgent, Map<Device, Double>>();
	private Map<INegotiatingAgent, INegotiatingAgent[]> criterion2elements =
		new HashMap<INegotiatingAgent, INegotiatingAgent[]>();

	@Override
	public void registerMark(
		INegotiatingAgent agent,
		Device device,
		double mark
	) {
		if (mark > 1 + EPS) {
			throw new IllegalArgumentException("Unexpected: mark > 1");
		}
		Map<Device, Double> dev2mark = ag2mark.get(agent);
		if (dev2mark == null) {
			dev2mark = new HashMap<Device, Double>();
			ag2mark.put(agent, dev2mark);
		}
		dev2mark.put(device, mark);
	}

	@Override
	public double[] getMarks(INegotiatingAgent criterionGroupAgent, Device device) {
		INegotiatingAgent[] criterionElements = criterion2elements.get(criterionGroupAgent);
		if (criterionElements == null) {
			return EMPTY_MARKS;
		}
		int elementsCount = criterionElements.length;
		double[] result = new double[elementsCount];
		for (int i = 0; i < elementsCount; i++) {
			Map<Device, Double> dev2mark = ag2mark.get(criterionElements[i]);
			if (dev2mark == null || !dev2mark.containsKey(device)) {
				result[i] = defaultVal;
			} else {
				result[i] = dev2mark.get(device);
			}
		}
		return result;
	}

	@Override
	public void registerCriterionAval(
		INegotiatingAgent criterionAgent,
		Device dev,
		double mark
	) {
		Map<Device, Double> dev2mark = crag2devs.get(criterionAgent);
		if (dev2mark == null) {
			dev2mark = new HashMap<Device, Double>();
			crag2devs.put(criterionAgent, dev2mark);
		}
		dev2mark.put(dev, mark);
	}

	@Override
	public double getCriterionAval(INegotiatingAgent criterionAgent, Device dev) {
		Map<Device, Double> dev2mark = crag2devs.get(criterionAgent);
		if (dev2mark == null) {
			return defaultVal;
		}
		if (dev2mark.containsKey(dev)) {
			return dev2mark.get(dev);
		} else {
			return defaultVal;
		}
	}

	@Override
	public Iterable<INegotiatingAgent> getCriterions() {
		return criterion2elements.keySet();
	}

	public void registerCriterionGroup(
		INegotiatingAgent markGoodPhone,
		INegotiationCondition goodPhone
	) {
		criterion2elements.put(
			markGoodPhone,
			goodPhone.getAgents().toArray(INegotiatingAgent.EMPTY_ARRAY)
		);
	}
}

class ChooseDevicesNegotiation implements ICommandConstruction {
	@Override
	public void execute(IExecutionContext context) {
		throw new UnsupportedOperationException("not implemented");
	}
}

abstract class BaseNegotiateDevSetAgent
	extends BaseNegotiationAgent
	implements ICommandConstruction
{
	private static final DecimalFormat MRK_FORMAT;
	static {
		MRK_FORMAT = new DecimalFormat();
		MRK_FORMAT.setMaximumFractionDigits(2);
		DecimalFormatSymbols dfs = MRK_FORMAT.getDecimalFormatSymbols();
		dfs.setDecimalSeparator('.');
		MRK_FORMAT.setDecimalFormatSymbols(dfs);
	}

	DeviceNegotiation negotiationState =
		DeviceNegotiation.COMPUTE_CHARACTIRISTICS;
	private StringBuilder sb;

	protected BaseNegotiateDevSetAgent(StringBuilder sb) {
		this.sb = sb;
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean negotiate(INegotiationContext context) {
		IDeviceMarksServ devMarks =
			context.getService(IDeviceMarksServ.class);
		IExecuteForService<Device> forServ =
			context.getService(IExecuteForService.class);
		Device curDev = forServ.getCurrentElem();

		String agentName = BaseUtils.getAbbreviationClassName(this.getClass());

		switch (negotiationState) {
		case COMPUTE_CHARACTIRISTICS:
			negotiationState = DeviceNegotiation.COLLECT_COMPUTATIONS;
			return true;

		case COLLECT_COMPUTATIONS: // TODOLONG: implement FA-system
			double minMark = Double.MAX_VALUE;
			for (double mark : devMarks.getMarks(this, curDev)) {
				minMark = Math.min(minMark, mark);
			}
			devMarks.registerCriterionAval(this, curDev, minMark);
			sb.append(";\nag:").append(agentName).append("-aval:")
				.append(curDev.getName()).append("=")
				.append(MRK_FORMAT.format(minMark));
			negotiationState = DeviceNegotiation.FIND_FRIENDS;
			return true;

		case FIND_FRIENDS:
			IDevCollectionsServ dCollServ =
				context.getService(IDevCollectionsServ.class);
			for (Set<Device> dset : dCollServ.getCollections()) {
				if (
					!dset.contains(curDev) &&
					myMarkIsWorst(dset, devMarks) > 0.7 &&
					getMyMark(dset, devMarks) < 0.7
				) {
					Set<Device> extendedSet = new HashSet<Device>(dset);
					extendedSet.add(curDev);

					sb.append(";\nag:").append(agentName).append("hasWrstMrk4:")
						.append(formatDSet(dset)).append("mrk=")
						.append(MRK_FORMAT.format(getMyMark(dset, devMarks)));
					if (getMyMark(dset, devMarks) < getMyMark(extendedSet, devMarks)) {
						sb.append(",addsDev:").append(curDev.getName()).append(",newMrk=")
							.append(MRK_FORMAT.format(getMyMark(extendedSet, devMarks)));
						dCollServ.addCollection(extendedSet);
					} else {
						sb.append(",CNCL:[")
							.append(MRK_FORMAT.format(getMyMark(extendedSet, devMarks)))
							.append("]");
					}
				}
			}
			Set<Device> propSingletone = BaseUtils.asSet(curDev);
			if (getMyMark(propSingletone, devMarks) > 0.45) {
				dCollServ.addCollection(propSingletone);
			}
			negotiationState = DeviceNegotiation.COMPUTE_CHARACTIRISTICS;
			return false;

		default:
			throw new UnsupportedOperationException("Invalid state");
		}
	}

	private String formatDSet(Set<Device> dset) {
		String[] names = new String[dset.size()];
		int i = 0;
		for (Device dev : dset) {
			names[i++] = dev.getName();
		}
		Arrays.sort(names);
		return Arrays.toString(names);
	}

	private double getMyMark(Set<Device> devSet, IDeviceMarksServ devMarks) {
		return getCriterionsMark(devSet, this, devMarks);
	}

	private static double getCriterionsMark(
		Set<Device> devSet,
		INegotiatingAgent criterionAgent,
		IDeviceMarksServ devMarks
	) {
		double result = -Double.MAX_VALUE;
		for (Device dev : devSet) {
			double mark = devMarks.getCriterionAval(criterionAgent, dev);
			result = Math.max(result, mark);
		}
		return result;
	}

	private double myMarkIsWorst(Set<Device> dset, IDeviceMarksServ devMarks) {
		double worstMark = Double.MAX_VALUE;
		for (INegotiatingAgent criterionAgent : devMarks.getCriterions()) {
			double theMark = getCriterionsMark(dset, criterionAgent, devMarks);
			worstMark = Math.min(worstMark, theMark);
		}
		double myMark = getMyMark(dset, devMarks);
		double result = worstMark / myMark;
		if (Double.isNaN(result)) {
			result = 1.0;
		}
		return result;
	}
}

class MarkGoodPhoneCmd
	extends BaseNegotiateDevSetAgent
{
	protected MarkGoodPhoneCmd(StringBuilder sb) {
		super(sb);
	}

	@Override
	public void execute(IExecutionContext context) {
	}
}

class MarkVeryGoodPlayerCmd
	extends BaseNegotiateDevSetAgent
{
	protected MarkVeryGoodPlayerCmd(StringBuilder sb) {
		super(sb);
	}

	@Override
	public void execute(IExecutionContext context) {
	}
}

class MarkGoodEnoughtPhotoDevCmd
	extends BaseNegotiateDevSetAgent
{
	protected MarkGoodEnoughtPhotoDevCmd(StringBuilder sb) {
		super(sb);
	}

	@Override
	public void execute(IExecutionContext context) {
	}
}

enum Quality {
	GOOD, VERY_GOOD, GOOD_ENOUGH, NOT_TOO_EXPENSIVE

}

class HasUsefulOrganiserCondition implements INegotiationCondition, INegotiatingAgent {
	@Override
	public List<INegotiatingAgent> getAgents() {
		return Arrays.<INegotiatingAgent>asList(this);
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean negotiate(INegotiationContext context) {
		IExecuteForService<Device> forServ =
			context.getService(IExecuteForService.class);

		Device device = forServ.getCurrentElem();
		boolean[] organiserFeatures = new boolean[] {
			device.hasAlarm,
			device.hasReminder,
		};

		int features = 0;
		int totalFeatures = 0;
		for (boolean feature : organiserFeatures) {
			if (feature) {
				features++;
			}
			totalFeatures++;
		}

		double orgIsUseful = (double)features / totalFeatures;

		IDeviceMarksServ devMarks =
			context.getService(IDeviceMarksServ.class);
		devMarks.registerMark(this, device, orgIsUseful);

		return false;
	}

	@Override
	public void onNegotiationStart(INegotiationContext context) {
	}
}

class SndRecvSMSCondition implements INegotiationCondition, INegotiatingAgent {
	@Override
	public List<INegotiatingAgent> getAgents() {
		return Arrays.<INegotiatingAgent>asList(this);
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean negotiate(INegotiationContext context) {
		IExecuteForService<Device> forServ =
			context.getService(IExecuteForService.class);

		Device device = forServ.getCurrentElem();
		boolean sndRecvSMS = device.sndRecvSMS;
		double sndRecvSMSDbl = sndRecvSMS ? 0.9 : 0.1;

		IDeviceMarksServ devMarks =
			context.getService(IDeviceMarksServ.class);
		devMarks.registerMark(this, device, sndRecvSMSDbl);

		return false;
	}

	@Override
	public void onNegotiationStart(INegotiationContext context) {
	}
}

class StrongSignalCondition implements INegotiationCondition, INegotiatingAgent {
	private double strong = 0.70;

	@Override
	public List<INegotiatingAgent> getAgents() {
		return Arrays.<INegotiatingAgent>asList(this);
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean negotiate(INegotiationContext context) {
		IExecuteForService<Device> forServ =
			context.getService(IExecuteForService.class);
		Device device = forServ.getCurrentElem();
		double signalStr = device.signalStrongness;
		double signalIsStrong = signalStr / strong;
		signalIsStrong = Math.min(signalIsStrong, 1.0);

		IDeviceMarksServ devMarks =
			context.getService(IDeviceMarksServ.class);
		devMarks.registerMark(this, device, signalIsStrong);

		return false;
	}

	@Override
	public void onNegotiationStart(INegotiationContext context) {
	}
}

class HasStrongDynamic implements INegotiationCondition, INegotiatingAgent {
	private double desiredQuality = 100;

	@Override
	public List<INegotiatingAgent> getAgents() {
		return Arrays.<INegotiatingAgent>asList(this);
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean negotiate(INegotiationContext context) {
		IExecuteForService<Device> forServ =
			context.getService(IExecuteForService.class);

		Device device = forServ.getCurrentElem();
		double dynamicQuality = device.reproducerQuality;
		double stongDynamic = dynamicQuality / desiredQuality;

		IDeviceMarksServ devMarks =
			context.getService(IDeviceMarksServ.class);
		devMarks.registerMark(this, device, stongDynamic);

		return false;
	}

	@Override
	public void onNegotiationStart(INegotiationContext context) {
	}
}

class AbleToPlayMp3 implements INegotiationCondition, INegotiatingAgent {
	@Override
	public List<INegotiatingAgent> getAgents() {
		return Arrays.<INegotiatingAgent>asList(this);
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean negotiate(INegotiationContext context) {
		IExecuteForService<Device> forServ =
			context.getService(IExecuteForService.class);

		Device device = forServ.getCurrentElem();
		boolean ableToPlayMp3 = device.ableToPlayMp3;
		double ableToPlayMp3Dbl = ableToPlayMp3 ? 0.9 : 0.1;

		IDeviceMarksServ devMarks =
			context.getService(IDeviceMarksServ.class);
		devMarks.registerMark(this, device, ableToPlayMp3Dbl);


		return false;
	}

	@Override
	public void onNegotiationStart(INegotiationContext context) {
	}
}

class MemoryIsBigEnought implements INegotiationCondition, INegotiatingAgent {
	private double desiredMemorySize =  500;

	@Override
	public List<INegotiatingAgent> getAgents() {
		return Arrays.<INegotiatingAgent>asList(this);
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean negotiate(INegotiationContext context) {
		IExecuteForService<Device> forServ =
			context.getService(IExecuteForService.class);

		Device device = forServ.getCurrentElem();
		double memorySize = device.musicMemorySize;
		double enoughtMem = memorySize / desiredMemorySize;

		IDeviceMarksServ devMarks =
			context.getService(IDeviceMarksServ.class);
		enoughtMem = Math.min(enoughtMem, 1.0);
		devMarks.registerMark(this, device, enoughtMem);

		return false;
	}

	@Override
	public void onNegotiationStart(INegotiationContext context) {
	}
}

class ResolutionIsLargeEnought implements INegotiationCondition, INegotiatingAgent {
	private double desiredResolutionX = 1200;
	private double desiredResolutionY = 800;

	@Override
	public List<INegotiatingAgent> getAgents() {
		return Arrays.<INegotiatingAgent>asList(this);
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean negotiate(INegotiationContext context) {
		IExecuteForService<Device> forServ =
			context.getService(IExecuteForService.class);

		Device device = forServ.getCurrentElem();
		double resolutionX = device.photoResolutionX;
		double resolutionY = device.photoResolutionY;
		double resolutionIsGood =
			(resolutionX / desiredResolutionX) *
			(resolutionY / desiredResolutionY);

		IDeviceMarksServ devMarks =
			context.getService(IDeviceMarksServ.class);
		resolutionIsGood = Math.min(resolutionIsGood, 1.0);
		devMarks.registerMark(this, device, resolutionIsGood);

		return false;
	}

	@Override
	public void onNegotiationStart(INegotiationContext context) {
	}
}

class ContrastIsGoodEnought implements INegotiationCondition, INegotiatingAgent {
	private double desiredMonContrast = 2345;

	@Override
	public List<INegotiatingAgent> getAgents() {
		return Arrays.<INegotiatingAgent>asList(this);
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean negotiate(INegotiationContext context) {
		IExecuteForService<Device> forServ =
			context.getService(IExecuteForService.class);

		Device device = forServ.getCurrentElem();
		double monitorContrast = device.photoContrast;
		double contrastIsGood = (monitorContrast / desiredMonContrast);

		IDeviceMarksServ devMarks =
			context.getService(IDeviceMarksServ.class);
		devMarks.registerMark(this, device, contrastIsGood);

		return false;
	}

	@Override
	public void onNegotiationStart(INegotiationContext context) {
	}
}

class AvaluatePriceCondition implements INegotiationCondition, INegotiatingAgent {
//	private double budget = 350;

	@Override
	public List<INegotiatingAgent> getAgents() {
		return Arrays.<INegotiatingAgent>asList(this);
	}

//	@SuppressWarnings("unchecked")
	@Override
	public boolean negotiate(INegotiationContext context) {
//		IExecuteForService<Device> forServ =
//			context.getService(IExecuteForService.class);

//		Device device = forServ.getCurrentElem();
//		double price = device.price;

		return false;
	}

	@Override
	public void onNegotiationStart(INegotiationContext context) {
	}
}

public class TestDeviceChoose extends BaseTestCase {
	private static final List<Device> DEFAULT_DEVICES = Arrays.asList(
		new Device(
			"4AllEx",
			0.85, true,
			true, true,
			90.0, 500, true,
			1200, 800,
			1234,
			800
		),
		new Device(
			"PhnVChp",
			0.8, true,
			true, false,
			10.0, 0, false,
			0, 0,
			0,
			70
		),
		new Device(
			"PhtOnl",
			0.0, false,
			false, false,
			0.0, 0, false,
			3000, 2000,
			2345,
			700
		),
		new Device(
			"Mp3Onl",
			0.0, false,
			false, false,
			100.0, 2048, true,
			0, 0,
			0,
			100
		),
		new Device(
			"4PhnMp3",
			0.8, true,
			true, true,
			70.0, 1500, true,
			0, 0,
			0,
			200
		)
	);

	public void testDeviceChoose() {
		StringBuilder sb = new StringBuilder();
		ICommandConstruction fuzzyChooseDevice =
			getChooseDeviceFuzzyCode(
				DEFAULT_DEVICES,
				sb
			);

		fuzzyChooseDevice.execute(IExecutionContext.EMPTY);

		/**
		 * not very reliable assert ... if wails -- do not worry too much 8-)
		 */
		assertEquals(
			";\n" +
			"ag:MGPC-aval:4AllEx=0.9;\n" +
			"ag:MVGPC-aval:4AllEx=0.9;\n" +
			"ag:MGEPDC-aval:4AllEx=0.53;\n" +
			"ag:MGPC-aval:PhnVChp=0.5;\n" +
			"ag:MVGPC-aval:PhnVChp=0;\n" +
			"ag:MGEPDC-aval:PhnVChp=0;\n" +
			"ag:MGEPDChasWrstMrk4:[4AllEx]mrk=0.53,CNCL:[0.53];\n" +
			"ag:MGPC-aval:PhtOnl=0;\n" +
			"ag:MVGPC-aval:PhtOnl=0;\n" +
			"ag:MGEPDC-aval:PhtOnl=1;\n" +
			"ag:MVGPChasWrstMrk4:[PhnVChp]mrk=0,CNCL:[0];\n" +
			"ag:MGEPDChasWrstMrk4:[4AllEx]mrk=0.53,addsDev:PhtOnl,newMrk=1;\n" +
			"ag:MGEPDChasWrstMrk4:[PhnVChp]mrk=0,addsDev:PhtOnl,newMrk=1;\n" +
			"ag:MGPC-aval:Mp3Onl=0;\n" +
			"ag:MVGPC-aval:Mp3Onl=0.9;\n" +
			"ag:MGEPDC-aval:Mp3Onl=0;\n" +
			"ag:MGPChasWrstMrk4:[PhtOnl]mrk=0,CNCL:[0];\n" +
			"ag:MVGPChasWrstMrk4:[PhnVChp]mrk=0,addsDev:Mp3Onl,newMrk=0.9;\n" +
			"ag:MVGPChasWrstMrk4:[PhnVChp, PhtOnl]mrk=0,addsDev:Mp3Onl,newMrk=0.9;\n" +
			"ag:MVGPChasWrstMrk4:[PhtOnl]mrk=0,addsDev:Mp3Onl,newMrk=0.9;\n" +
			"ag:MGEPDChasWrstMrk4:[4AllEx]mrk=0.53,CNCL:[0.53];\n" +
			"ag:MGEPDChasWrstMrk4:[PhnVChp]mrk=0,CNCL:[0];\n" +
			"ag:MGPC-aval:4PhnMp3=0.9;\n" +
			"ag:MVGPC-aval:4PhnMp3=0.7;\n" +
			"ag:MGEPDC-aval:4PhnMp3=0;\n" +
			"ag:MGPChasWrstMrk4:[PhtOnl]mrk=0,addsDev:4PhnMp3,newMrk=0.9;\n" +
			"ag:MGPChasWrstMrk4:[Mp3Onl, PhnVChp, PhtOnl]mrk=0.5,addsDev:4PhnMp3,newMrk=0.9;\n" +
			"ag:MGPChasWrstMrk4:[Mp3Onl, PhtOnl]mrk=0,addsDev:4PhnMp3,newMrk=0.9;\n" +
			"ag:MGPChasWrstMrk4:[Mp3Onl]mrk=0,addsDev:4PhnMp3,newMrk=0.9;\n" +
			"ag:MVGPChasWrstMrk4:[PhnVChp]mrk=0,addsDev:4PhnMp3,newMrk=0.7;\n" +
			"ag:MVGPChasWrstMrk4:[PhnVChp, PhtOnl]mrk=0,addsDev:4PhnMp3,newMrk=0.7;\n" +
			"ag:MVGPChasWrstMrk4:[PhtOnl]mrk=0,addsDev:4PhnMp3,newMrk=0.7;\n" +
			"ag:MGEPDChasWrstMrk4:[4AllEx]mrk=0.53,CNCL:[0.53];\n" +
			"ag:MGEPDChasWrstMrk4:[PhnVChp]mrk=0,CNCL:[0];\n" +
			"ag:MGEPDChasWrstMrk4:[Mp3Onl, PhnVChp]mrk=0,CNCL:[0];\n" +
			"ag:MGEPDChasWrstMrk4:[Mp3Onl]mrk=0,CNCL:[0]",
			sb.toString()
		);

//		List<Device> computationResult = extractTheResult(fuzzyChooseDevice);
//		assertEquals(-1, computationResult.size());
	}

	private static ICommandConstruction getChooseDeviceFuzzyCode(
		List<Device> list,
		StringBuilder sb
	) {
		NegotiationForAbsEach<Device> mainLoop =
 			new NegotiationForAbsEach<Device>(list);

		// ### ### ### ### ### ###

		// conditions
		INegotiationCondition isGoodPhone =
			newIsGoodPhoneCondition();
		INegotiationCondition isVeryGoodPlayer =
			newIsVeryGoodPlayerCondition();
		INegotiationCondition isGoodEnoughPhotoDev =
			newIsGoodEnoughPhotoDevCondition();
		INegotiationCondition isNotTooExpensive =
			// DECIDE: how to interpret NOT
			new AvaluatePriceCondition();

		NegotiationAndCondition goodForAllCondition =
			new NegotiationAndCondition(
				isGoodPhone,
				isVeryGoodPlayer,
				isGoodEnoughPhotoDev,
				isNotTooExpensive
			);

		// commands
		MarkGoodPhoneCmd markGoodPhone =
			new MarkGoodPhoneCmd(sb);
		MarkVeryGoodPlayerCmd markVeryGoodPlayer =
			new MarkVeryGoodPlayerCmd(sb);
		MarkGoodEnoughtPhotoDevCmd markGoodEnoughtPhotoDev =
			new MarkGoodEnoughtPhotoDevCmd(sb);

		NegotiationIfConstruction ifGoodStructure =
			new NegotiationIfConstruction(
				goodForAllCondition,
				Arrays.<ICommandConstruction>asList(
					markGoodPhone,
					markVeryGoodPlayer,
					markGoodEnoughtPhotoDev
				),
				ICommandConstruction.EMPTY_LIST
			);
		ifGoodStructure.extractAgentsFromConstructions();
		mainLoop.addBodyCommands(ifGoodStructure);

		// ### ### ### ### ### ###

		mainLoop.extractAgentsFromConstructions();
		mainLoop.getAgents().add(new StoreDevCollections());
		StoreDeviceMarksAgent storeMarksAgent = new StoreDeviceMarksAgent();
		storeMarksAgent.registerCriterionGroup(
			markGoodPhone,
			isGoodPhone
		);
		storeMarksAgent.registerCriterionGroup(
			markVeryGoodPlayer,
			isVeryGoodPlayer
		);
		storeMarksAgent.registerCriterionGroup(
			markGoodEnoughtPhotoDev,
			isGoodEnoughPhotoDev
		);
		mainLoop.getAgents().add(storeMarksAgent);

		return mainLoop;
	}

	private static INegotiationCondition newIsGoodEnoughPhotoDevCondition() {
		INegotiationCondition result = new NegotiationAndCondition(
			new ResolutionIsLargeEnought(),
			new ContrastIsGoodEnought()
		);
		return result;
	}

	private static INegotiationCondition newIsVeryGoodPlayerCondition() {
		INegotiationCondition result = new NegotiationAndCondition(
			new MemoryIsBigEnought(),
			new AbleToPlayMp3(),
			new HasStrongDynamic()
		);
		return result;
	}

	private static INegotiationCondition newIsGoodPhoneCondition() {
		NegotiationAndCondition result = new NegotiationAndCondition(
			new StrongSignalCondition(),
			new SndRecvSMSCondition(),
			new HasUsefulOrganiserCondition()
		);
		return result;
	}
}
